package interview_03_04

import (
	"errors"
	"fmt"
	"sync"
)

type MyQueue struct {
	stackIn  MyStack
	stackOut MyStack
}

/** Initialize your data structure here. */
func Constructor() MyQueue {
	return MyQueue{}
}

/** Push element x to the back of queue. */
func (q *MyQueue) Push(x int) {
	for true {
		el, err := q.stackOut.Pop()
		if err != nil {
			break
		}
		q.stackIn.Push(el)
	}
	q.stackIn.Push(x)
	for true {
		el, err := q.stackIn.Pop()
		if err != nil {
			break
		}
		q.stackOut.Push(el)
	}
}

/** Removes the element from in front of queue and returns that element. */
func (q *MyQueue) Pop() int {
	el, _ := q.stackOut.Pop()
	return el
}

/** Get the front element. */
func (q *MyQueue) Peek() int {
	el, _ := q.stackOut.Top()
	return el
}

/** Returns whether the queue is empty. */
func (q *MyQueue) Empty() bool {
	return q.stackOut.Empty()
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * obj := Constructor();
 * obj.Push(x);
 * param_2 := obj.Pop();
 * param_3 := obj.Peek();
 * param_4 := obj.Empty();
 */

type MyStack struct {
	items []int
	l     sync.Mutex
}

func (m *MyStack) Push(n int) {
	m.l.Lock()
	defer m.l.Unlock()
	m.items = append(m.items, n)
}

func (m *MyStack) Pop() (int, error) {
	m.l.Lock()
	defer m.l.Unlock()
	if len(m.items) == 0 {
		return 0, errors.New("empty of stack")
	}
	el := m.items[len(m.items)-1]
	m.items = m.items[:len(m.items)-1]
	return el, nil
}

func (m *MyStack) Top() (int, error) {
	m.l.Lock()
	defer m.l.Unlock()
	if len(m.items) == 0 {
		return 0, errors.New("empty of stack")
	}
	el := m.items[len(m.items)-1]
	return el, nil
}

func (m *MyStack) Size() int {
	return len(m.items)
}

func (m *MyStack) Empty() bool {
	if len(m.items) == 0 {
		return true
	}
	return false
}

func (m MyStack) Show() {
	fmt.Println(m.items)
}

func operationQueue1() bool {
	q := Constructor()
	q.Push(1)
	q.Push(2)

	fmt.Println(q.Peek())  // 返回 1
	fmt.Println(q.Pop())   // 返回 1
	fmt.Println(q.Empty()) // 返回 false

	return true
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * obj := Constructor();
 * obj.Push(x);
 * param_2 := obj.Pop();
 * param_3 := obj.Peek();
 * param_4 := obj.Empty();
 */
